/**
 * @author Justin Akin
 * @version Action Pals Core Library 0.1.0
 */
package com.actionpals.components.controls
{	
	import com.actionpals.base.BaseSprite;
	import com.actionpals.broadcasters.WheelScrollCast;
	import com.actionpals.enum.Align;
	import com.actionpals.events.WheelScrollEvent;
	import com.actionpals.graphics.RectangleShape;
	import com.actionpals.managers.DragManager;
	import com.actionpals.managers.drag.DragInfo;
	
	import flash.display.InteractiveObject;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Rectangle;
	
	/**  
	 * The BaseSlider provides functionality for other interactive components such as a ScrollBar
	 * or VolumeSlider etc.  It allows for it's own shapes to be used solely as hitAreas so that
	 * sub-classes may provide a custom view.
	 */
	public class SimpleSlider extends BaseSprite
	{	
	
	//--------------------------------------------------------------------------------
	//
	//	Protected Member variables
	//
	//--------------------------------------------------------------------------------
		
		protected var _percentInc:Number;
		protected var _track:RectangleShape;
		protected var _handle:RectangleShape;
		protected var _handleData:DragInfo;
		protected var _activeScrollArea:InteractiveObject;
		protected var _eventsBubble:Boolean = false;
		protected var _eventsCancelable:Boolean = false;
		protected var _dragManager:DragManager;
	
	//--------------------------------------------------------------------------------
	//
	//	Constructor
	//
	//--------------------------------------------------------------------------------
	
		/**
		 * Constructor.
		 * 
		 * @param w		The Track used to contain the _handle drag.
		 * @param h		The Handle to be dragged.
		 */
		public function SimpleSlider( w:Number, h:Number, activeScrollArea:InteractiveObject = null )
		{	
			_activeScrollArea = activeScrollArea;
			_dragManager = DragManager.dragManager;
			
			super();
			
			addTrack( w, h );
			addHandle( w, h );
			
			addEventListener( Event.ADDED_TO_STAGE, onAddedToStage );	
		}
		
	//--------------------------------------------------------------------------------
	//
	//	Public Functions
	//
	//-------------------------------------------------------------------------------- 
		
		/**
		 * Dispatches an UpdateEvent.UPDATE event
		 */
		public function dispatchUpdate():void
		{
			dispatchEvent( new Event( Event.CHANGE, _eventsBubble, _eventsCancelable ) );
		}
		
		/**
		 * Moves the _handle to a specified x and y position and calls dispatchUpdate.
		 * 
		 * @param xPos	The new x position.
		 * @param yPos	The new y position.
		 */
		public function updateHandle( xPos:Number = undefined, yPos:Number = undefined ):void
		{	
			var newX:Number = ( xPos-xPos == 0 ) ? xPos : _handle.x;
			var newY:Number = ( yPos-yPos == 0 ) ? yPos : _handle.y;
			var bounds:Rectangle = _track.getRect( this );
			var temp:Rectangle = new Rectangle( newX, newY, _handle.width, _handle.height );
			
			if( temp.right > bounds.right ) temp.x = bounds.right - temp.width;
			if( temp.left < bounds.left ) temp.x = bounds.x;
			if( temp.bottom > bounds.bottom ) temp.y = bounds.bottom - temp.height;
			if( temp.top < bounds.top ) temp.y = bounds.y;
			
			_handle.x = temp.x;
			_handle.y = temp.y;
			
			dispatchUpdate();
		}
		
		/**
		 * Moves the handle based on a percentage value from 0 - 1.
		 * 
		 * @param percent		The percentage to move the handle.
		 * @param orientation	Represents from which direction to calculate percentage.
		 */
		public function moveHandleToPercent( percent:Number, orientation:String = Align.LEFT ):void
		{				
			var hRect:Rectangle = _handle.getRect( this );
			var tRect:Rectangle = _track.getRect( this );
			var total:Number;
			var newX:Number;
			var newY:Number;
			
			switch( orientation )
			{		
				case Align.TOP :
				
					total = tRect.height - hRect.height;
					newY = total * percent;
					break;
					
				case Align.BOTTOM :
				
					total = tRect.width - hRect.width;
					newY = total - ( total * percent );
					break;
				
				case Align.LEFT :
					
					total = tRect.width - hRect.width;
					newX = total * percent;
					break;
					
				case Align.RIGHT :
				
					total = tRect.width - hRect.width;
					newX = total - ( total * percent );
					break;
					
				case Align.VCENTER :
				
					total = ( tRect.height - hRect.height ) / 2;
					newY = 1 - ( total * percent );
					break;
					
				case Align.HCENTER :
				
					total = ( tRect.width - hRect.width ) / 2;
					newY = 1 - ( total - ( total * percent ) );
					break;
			}
			
			updateHandle( newX, newY );
		}
		
	//--------------------------------------------------------------------------------
	//
	//	Getters and Setters
	//
	//--------------------------------------------------------------------------------
		
		public function set eventsBubble( val:Boolean ):void
		{
			_eventsBubble = val;
		}
		
		public function set eventsCancelable( val:Boolean ):void
		{
			_eventsCancelable = val;
		}
		
		public function get percentFromTop():Number
		{
			var percent:Number = 0;
			var hRect:Rectangle = _handle.getRect( this );
			var tRect:Rectangle = _track.getRect( this );
			var val1:Number = hRect.top - tRect.top;
			var val2:Number = tRect.height - hRect.height;
			var value:Number = val1 / val2;
			
			return isNaN( value ) ? 0 : value;
		}
		
		public function get percentFromBottom():Number
		{
			var percent:Number = 0;
			var hRect:Rectangle = _handle.getRect( this );
			var tRect:Rectangle = _track.getRect( this );
			var val1:Number = tRect.bottom - hRect.bottom;
			var val2:Number = tRect.height - hRect.height;
			var value:Number = val1 / val2;
			
			return isNaN( value ) ? 0 : value;
		}
		
		public function get percentFromLeft():Number
		{
			var percent:Number = 0;
			var hRect:Rectangle = _handle.getRect( this );
			var tRect:Rectangle = _track.getRect( this );
			var val1:Number = hRect.left - tRect.left;
			var val2:Number = tRect.width - hRect.width;
			var value:Number = val1 / val2;
			
			return isNaN( value ) ? 0 : value;
		}
		
		public function get percentFromRight():Number
		{
			var percent:Number = 0;
			var hRect:Rectangle = _handle.getRect( this );
			var tRect:Rectangle = _track.getRect( this );
			var val1:Number = tRect.right - hRect.right;
			var val2:Number = tRect.width - hRect.width;
			var value:Number = val1 / val2;
			
			return isNaN( value ) ? 0 : value;
		}
		
		public function get handleHeight():Number
		{
			return _handle.height;
		}
		
		public function set handleHeight( h:Number ):void
		{	
			if( h > _track.height ) h = _track.height;
			
			_handle.height = h;
		}
		
		public function get handleWidth():Number
		{
			return _handle.width;
		}
		
		public function set handleWidth( w:Number ):void
		{	
			if( w > _track.width ) w = _track.width;	
			_handle.width = w;
		}
		
		public function get trackWidth():Number
		{
			return _track.width;
		}
		
		public function set trackWidth( w:Number ):void
		{
			_track.width = w;
		}
		
		public function get trackHeight():Number
		{
			return _track.height;
		}
		
		public function set trackHeight( h:Number ):void
		{
			_track.height = h;
		}
		
	//--------------------------------------------------------------------------------
	//	Protected Functions
	//--------------------------------------------------------------------------------
		
		protected function addTrack( w:Number, h:Number ):void
		{
			_track = addChild( new RectangleShape( w, h ) ) as RectangleShape;
		}
		
		protected function addHandle( w:Number, h:Number ):void
		{
			_handle = addChild( new RectangleShape( w, h, 0, 0xFF ) ) as RectangleShape;
		}
		
		protected function addListeners():void
		{	
			var area:InteractiveObject = _activeScrollArea ? _activeScrollArea : stage;
			var wheelScrollCast:WheelScrollCast = new WheelScrollCast( area );
			
			wheelScrollCast.addEventListener( WheelScrollEvent.WHEEL_UP, onWheelUp );
			wheelScrollCast.addEventListener( WheelScrollEvent.WHEEL_DOWN, onWheelDown );
			
			_handle.addEventListener( MouseEvent.MOUSE_DOWN, onHandleDown );	
			_track.addEventListener( MouseEvent.CLICK, onTrackClick );	
		}
		
	//--------------------------------------------------------------------------------
	//	Event Handlers
	//--------------------------------------------------------------------------------
		
		/**
		 * This event _handler was implemented so that the stage or root property of the
		 * display list could be accessed.  An error is thrown if you try to access one of
		 * those properties before this class has been added to the dispay list.  This _handler
		 * is fired off when this class is added to the display list.
		 * 
		 * @param event		An Event object.
		 */
		protected function onAddedToStage( event:Event ):void
		{
			addListeners();				
		}
		
		/**
		 * Called when _track is clicked.  Updates _handle's x and y position.  The closest
		 * side of the _handle is moved to the new position.  For example if you clicked to an
		 * x position that was greater than _handle.x then _handle's right side would be aligned
		 * with the new x position.
		 * 
		 * @param event		An Event object.
		 */
		protected function onTrackClick( event:MouseEvent ):void
		{	
			var temp:Rectangle = _handle.getRect( this );
			var xPos:Number = event.localX;
			var yPos:Number = event.localY;
			
			if( xPos > temp.x ) xPos -= temp.width;
			if( yPos > temp.y ) yPos -= temp.height;
			
			updateHandle( xPos, yPos );
		}
		
		/**
		 * Called when _handle is pressed down.  Allows _handle to drag within _track's area.
		 * 
		 * @param event		An Event object.
		 */
		protected function onHandleDown( event:MouseEvent ):void
		{	
			_dragManager.remove( _handle );
			_dragManager.add( _handle, _track.getBounds( this ) );
			
			stage.addEventListener( MouseEvent.MOUSE_MOVE, onHandleMove );
			stage.addEventListener( Event.MOUSE_LEAVE, onMouseLeave );
			stage.addEventListener( MouseEvent.MOUSE_UP, onMouseEventUp );	
		}
		
		/**
		 * Called when the Mouse is released.  Stops _handle from dragging.
		 * 
		 * @param event		An Event object.
		 */
		protected function onMouseEventUp( event:MouseEvent ):void
		{
			_dragManager.remove( _handle );
			stage.removeEventListener( MouseEvent.MOUSE_MOVE, onHandleMove );
			stage.removeEventListener( Event.MOUSE_LEAVE, onMouseLeave );
			stage.removeEventListener( MouseEvent.MOUSE_UP, onMouseEventUp );	
		}
		
		/**
		 * Called when _handle is dragged.  Calls dispatchUpdate.
		 * 
		 * @param event		An Event object.
		 */
		protected function onHandleMove( event:MouseEvent ):void
		{	
			if( event.buttonDown )
			{
				dispatchUpdate();
			}
			else
			{	
				_dragManager.remove( _handle );
				stage.removeEventListener( MouseEvent.MOUSE_MOVE, onHandleMove );
				stage.removeEventListener( Event.MOUSE_LEAVE, onMouseLeave );
				stage.removeEventListener( MouseEvent.MOUSE_UP, onMouseEventUp );	
			}
		}
		
		protected function onMouseLeave( event:Event ):void
		{	
			_dragManager.remove( _handle );
			stage.removeEventListener( MouseEvent.MOUSE_MOVE, onHandleMove );
			stage.removeEventListener( Event.MOUSE_LEAVE, onMouseLeave );
			stage.removeEventListener( MouseEvent.MOUSE_UP, onMouseEventUp );	
		}
			
		/**
		 * 
		 */
		protected function onWheelUp( event:WheelScrollEvent ):void
		{
			var percentInc:Number = .01;
			var inc:Number = event.delta * percentInc;
			var percent:Number = percentFromTop - inc;
			
			moveHandleToPercent( percent, Align.TOP );			
		}
			
		/**
		 * 
		 */
		protected function onWheelDown( event:WheelScrollEvent ):void
		{	
			var percentInc:Number = .01;
			var inc:Number = event.delta * percentInc;
			var percent:Number = percentFromTop - inc;
			
			moveHandleToPercent( percent, Align.TOP );			
		}			
	}	
}